/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.flowable.examples.processdefinitions;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.flowable.engine.impl.bpmn.deployer.ResourceNameUtil;
import org.flowable.engine.impl.test.PluggableFlowableTestCase;
import org.flowable.engine.repository.ProcessDefinition;
import org.junit.jupiter.api.Test;

/**
 * @author Tom Baeyens
 * @author Joram Barrez
 */
public class ProcessDefinitionsTest extends PluggableFlowableTestCase {

    private static final String NAMESPACE = "xmlns='http://www.omg.org/spec/BPMN/20100524/MODEL'";

    private static final String TARGET_NAMESPACE = "targetNamespace='http://activiti.org/BPMN20'";

    @Test
    public void testGetProcessDefinitions() {
        List<String> deploymentIds = new ArrayList<>();
        deploymentIds.add(deployProcessString(("<definitions " + NAMESPACE + " " + TARGET_NAMESPACE + ">" + "  <process id='IDR' name='Insurance Damage Report 1' />" + "</definitions>")));
        deploymentIds.add(deployProcessString(("<definitions " + NAMESPACE + " " + TARGET_NAMESPACE + ">" + "  <process id='IDR' name='Insurance Damage Report 2' />" + "</definitions>")));
        deploymentIds.add(deployProcessString(("<definitions " + NAMESPACE + " " + TARGET_NAMESPACE + ">" + "  <process id='IDR' name='Insurance Damage Report 3' />" + "</definitions>")));
        deploymentIds.add(deployProcessString(("<definitions " + NAMESPACE + " " + TARGET_NAMESPACE + ">" + "  <process id='EN' name='Expense Note 1' />" + "</definitions>")));
        deploymentIds.add(deployProcessString(("<definitions " + NAMESPACE + " " + TARGET_NAMESPACE + ">" + "  <process id='EN' name='Expense Note 2' />" + "</definitions>")));

        List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().orderByProcessDefinitionKey().asc().orderByProcessDefinitionVersion().desc().list();

        assertThat(processDefinitions).hasSize(5);

        ProcessDefinition processDefinition = processDefinitions.get(0);
        assertThat(processDefinition.getKey()).isEqualTo("EN");
        assertThat(processDefinition.getName()).isEqualTo("Expense Note 2");
        assertThat(processDefinition.getId()).startsWith("EN:2");
        assertThat(processDefinition.getVersion()).isEqualTo(2);

        processDefinition = processDefinitions.get(1);
        assertThat(processDefinition.getKey()).isEqualTo("EN");
        assertThat(processDefinition.getName()).isEqualTo("Expense Note 1");
        assertThat(processDefinition.getId()).startsWith("EN:1");
        assertThat(processDefinition.getVersion()).isEqualTo(1);

        processDefinition = processDefinitions.get(2);
        assertThat(processDefinition.getKey()).isEqualTo("IDR");
        assertThat(processDefinition.getName()).isEqualTo("Insurance Damage Report 3");
        assertThat(processDefinition.getId()).startsWith("IDR:3");
        assertThat(processDefinition.getVersion()).isEqualTo(3);

        processDefinition = processDefinitions.get(3);
        assertThat(processDefinition.getKey()).isEqualTo("IDR");
        assertThat(processDefinition.getName()).isEqualTo("Insurance Damage Report 2");
        assertThat(processDefinition.getId()).startsWith("IDR:2");
        assertThat(processDefinition.getVersion()).isEqualTo(2);

        processDefinition = processDefinitions.get(4);
        assertThat(processDefinition.getKey()).isEqualTo("IDR");
        assertThat(processDefinition.getName()).isEqualTo("Insurance Damage Report 1");
        assertThat(processDefinition.getId()).startsWith("IDR:1");
        assertThat(processDefinition.getVersion()).isEqualTo(1);

        Set<String> queryDeploymentIds = new HashSet<>();
        queryDeploymentIds.add(processDefinitions.get(0).getDeploymentId());
        queryDeploymentIds.add(processDefinitions.get(1).getDeploymentId());
        List<ProcessDefinition> queryProcessDefinitions = repositoryService.createProcessDefinitionQuery().deploymentIds(queryDeploymentIds).orderByProcessDefinitionKey().asc()
                .orderByProcessDefinitionVersion().desc().list();
        assertThat(queryProcessDefinitions)
                .extracting(ProcessDefinition::getKey, ProcessDefinition::getName)
                .containsExactlyInAnyOrder(
                        tuple("EN", "Expense Note 2"),
                        tuple("EN", "Expense Note 1")
                );

        queryDeploymentIds = new HashSet<>();
        queryDeploymentIds.add(processDefinitions.get(0).getDeploymentId());
        queryDeploymentIds.add(processDefinitions.get(3).getDeploymentId());
        queryDeploymentIds.add(processDefinitions.get(4).getDeploymentId());
        queryProcessDefinitions = repositoryService.createProcessDefinitionQuery().deploymentIds(queryDeploymentIds).list();
        assertThat(queryProcessDefinitions)
                .extracting(ProcessDefinition::getKey, ProcessDefinition::getName)
                .containsExactlyInAnyOrder(
                        tuple("EN", "Expense Note 2"),
                        tuple("IDR", "Insurance Damage Report 1"),
                        tuple("IDR", "Insurance Damage Report 2")
                );

        deleteDeployments(deploymentIds);
    }

    @Test
    public void testDeployIdenticalProcessDefinitions() {
        List<String> deploymentIds = new ArrayList<>();
        deploymentIds.add(deployProcessString(("<definitions " + NAMESPACE + " " + TARGET_NAMESPACE + ">" + "  <process id='IDR' name='Insurance Damage Report' />" + "</definitions>")));
        deploymentIds.add(deployProcessString(("<definitions " + NAMESPACE + " " + TARGET_NAMESPACE + ">" + "  <process id='IDR' name='Insurance Damage Report' />" + "</definitions>")));

        List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().orderByProcessDefinitionKey().asc().orderByProcessDefinitionVersion().desc().list();

        assertThat(processDefinitions).isNotNull();
        assertThat(processDefinitions)
                .extracting(ProcessDefinition::getKey, ProcessDefinition::getName, ProcessDefinition::getVersion)
                .containsExactly(
                        tuple("IDR", "Insurance Damage Report", 2),
                        tuple("IDR", "Insurance Damage Report", 1)
                );
        assertThat(processDefinitions.get(0).getId()).startsWith("IDR:2");
        assertThat(processDefinitions.get(1).getId()).startsWith("IDR:1");

        deleteDeployments(deploymentIds);
    }

    @Test
    public void testProcessDefinitionDescription() {
        String deploymentId = deployProcessString(("<definitions " + NAMESPACE + " " + TARGET_NAMESPACE + ">" + "  <process id='test' name='test'><documentation>This is a test</documentation></process></definitions>"));
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        assertThat(processDefinition.getDescription()).isEqualTo("This is a test");

        deleteDeployments(Collections.singletonList(deploymentId));
    }

    private String deployProcessString(String processString) {
        String resourceName = "xmlString." + ResourceNameUtil.BPMN_RESOURCE_SUFFIXES[0];
        return repositoryService.createDeployment().addString(resourceName, processString).deploy().getId();
    }

    private void deleteDeployments(Collection<String> deploymentIds) {
        for (String deploymentId : deploymentIds) {
            repositoryService.deleteDeployment(deploymentId);
        }
    }
}
